/*******************************************************************************
 *  Imixs IX Workflow Technology
 *  Copyright (C) 2003, 2008 Imixs Software Solutions GmbH,  
 *  http://www.imixs.com
 *  
 *  This program is free software; you can redistribute it and/or 
 *  modify it under the terms of the GNU General Public License 
 *  as published by the Free Software Foundation; either version 2 
 *  of the License, or (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful, 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 *  General Public License for more details.
 *  
 *  You can receive a copy of the GNU General Public
 *  License at http://www.gnu.org/licenses/gpl.html
 *  
 *  Contributors:  
 *  	Imixs Software Solutions GmbH - initial API and implementation
 *  	Ralph Soika
 *  
 *******************************************************************************/
package org.imixs.manik.webstat.web;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.component.UIParameter;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;

import org.imixs.manik.webstat.ejb.AccessLogService;
import org.imixs.manik.webstat.ejb.AnalyzerService;
import org.imixs.manik.webstat.jpa.AccessLog;
import org.imixs.manik.webstat.jpa.Analyzer;

/**
 * This backing bean is an example how to interact with the EntityService to
 * manage ItemCollections as instances of Entities
 * <p>
 * The bean provides an ItemCollectionAdapter to simplify to access properties
 * of the ItemCollection from an JSP page.
 * <p>
 * 
 * @see team.xhtml, teamlist.xhtml
 * 
 * @author rsoika
 * 
 */

@ManagedBean
@SessionScoped
public class AnalyzerMB {

	private long accessLogId;

	private Analyzer currentAnalyzer;

	private List<Analyzer> analyzers = null;

	private Navigator navRequest;
	private Navigator navReferrer;

	private String errorCode;
	private String timeRange; // indicates the timeRange Key
	private String activeTab;
	private String currentMonth;
	private String currentYear;

	private static Logger logger = Logger.getLogger("org.imixs.manik.webstat");

	// result sets
	private List<ArrayList<Object>> totalErrorsByCode = null;
	private List<ArrayList<Object>> totalErrors = null;
	private List<ArrayList<Object>> totalHitsReferrer = null;

	private List<ArrayList<Object>> totalHitsRequestURI = null;

	private List<ArrayList<Object>> browserType = null;
	private List<ArrayList<Object>> operatingsystem = null;
	private List<ArrayList<Object>> trendByDay = null;
	private List<ArrayList<Object>> trendByMonth = null;

	@EJB
	AccessLogService accessLogService;

	@EJB
	AnalyzerService anaylzerService;

	public AnalyzerMB() {
		super();

	}

	/**
	 * The init method is used to construct navigator objects
	 */
	@PostConstruct
	public void init() {
		//
		navRequest = new Navigator(30);
		navReferrer = new Navigator(30);

	}

	public Analyzer getAnalyzer() {
		if (currentAnalyzer == null)
			this.doCreate(null);
		return currentAnalyzer;
	}

	public void setAnalyzer(Analyzer currentanalyzer) {
		this.currentAnalyzer = currentanalyzer;
		// update the accessLogID now!
		if (currentAnalyzer.getAccessLog() != null)
			accessLogId = currentAnalyzer.getAccessLog().getId();

		// reset timeRange
		this.setTimeRange(currentAnalyzer.getTimeFrame());
		doResetResult(null);
	}

	/**
	 * returns true if the current logFormat provides a referrer info
	 * 
	 * @return
	 */
	public boolean isReferrerSupported() {
		if (currentAnalyzer == null || currentAnalyzer.getAccessLog() == null)
			return false;
		// only combined did support referrer info
		return (currentAnalyzer.getAccessLog().getLogformat() == AccessLog.ACCESS_FORMAT_COMBINED);
	}

	/**
	 * returns true if the current logFormat provides a UserAgent info
	 * 
	 * @return
	 */
	public boolean isUseragentSupported() {
		if (currentAnalyzer == null || currentAnalyzer.getAccessLog() == null)
			return false;
		// only combined did support referrer info
		return (currentAnalyzer.getAccessLog().getLogformat() == AccessLog.ACCESS_FORMAT_COMBINED);
	}

	public long getAccessLogId() {
		return accessLogId;
	}

	public void setAccessLogId(long accessLogId) {
		this.accessLogId = accessLogId;

		// update the accessLog now!
		if (currentAnalyzer != null)
			currentAnalyzer.setAccessLog(accessLogService
					.findAccessLog(accessLogId));
	}

	public String getActiveTab() {
		if (activeTab == null)
			activeTab = "tab1";
		return activeTab;
	}

	public void setActiveTab(String aactiveTab) {
		logger.info("Activtab = " + aactiveTab);
		this.activeTab = aactiveTab;
	}

	public void doRefreshTotalHits(ActionEvent event) {
		logger.info("doRefreshTotalHits...");
		this.doResetResult(event);

	}

	public String actionTotalHits() {
		logger.info("actionTotalHits...");
		return "";

	}

	/**
	 * This method is called by a jsf page to create a new instance of a team
	 * entity.
	 * 
	 * This method creates an empty ItemCollection
	 * 
	 * @param event
	 * @return
	 */
	public void doCreate(ActionEvent event) {
		logger.info("Create new Analyzer...");
		currentAnalyzer = new Analyzer();

	}

	/**
	 * saves the current Analyzer.
	 * 
	 * The method evaluates the Log-ID and updates the reference to the
	 * corresponding AccessLog.
	 * 
	 * 
	 * @param event
	 * @return
	 * @throws Exception
	 */
	public void doSave(ActionEvent event) throws Exception {
		// save domain now...
		currentAnalyzer.setAccessLog(accessLogService
				.findAccessLog(accessLogId));

		anaylzerService.save(currentAnalyzer);
		doReset(event);
	}

	/**
	 * resets the analyzers list
	 * 
	 * @return
	 */
	public void doReset(ActionEvent event) {
		analyzers = null;
		doResetResult(event);
	}

	/**
	 * resets the result sets
	 * 
	 * @return
	 */
	public void doResetResult(ActionEvent event) {

		totalErrors = null;
		totalErrorsByCode = null;
		totalHitsReferrer = null;
		totalHitsRequestURI = null;
		browserType = null;
		operatingsystem = null;
		trendByDay = null;
		trendByMonth = null;
	}

	/**
	 * this method is called by datatables to select an workitem
	 * 
	 * @return
	 */

	public String editAnalzyer() {
		return "/pages/analyzer";
	}

	/**
	 * this method removes the current selected worktiem from a view
	 * 
	 * @param event
	 * @throws Exception
	 */
	public String deleteAnalyzer() {
		try {
			anaylzerService.remove(currentAnalyzer);
		} catch (Exception e) {
			logger.severe(" Error deleting Analyzer - " + e.getMessage());
			e.printStackTrace();
		}

		doReset(null);

		return "/pages/analyzerlist";
	}

	public List<Analyzer> getAnalyzers() {
		if (analyzers == null)
			analyzers = anaylzerService.findAllAnalyzers();
		return analyzers;
	}

	/**
	 * returns an arrayList of Selectitems with all AccessLogs
	 * 
	 * @return
	 */
	public ArrayList<SelectItem> getAccessLogSelection() {

		ArrayList<SelectItem> logSelection = new ArrayList<SelectItem>();
		List<AccessLog> logs = this.accessLogService.getAccessLogs();
		for (AccessLog aitem : logs) {
			logSelection.add(new SelectItem(aitem.getId(), aitem.getName()));
		}

		return logSelection;
	}

	/**
	 * returns an arrayList of Selectitems with Date Ranges
	 * <p>
	 * <ul>
	 * <li>today
	 * <li>last 7 days
	 * <li>last 30 days
	 * <li>this year
	 * <li>this month
	 * <li>custom
	 * </ul>
	 * 
	 * @return
	 */
	public ArrayList<SelectItem> getTimeRangeSelection() {

		ArrayList<SelectItem> logSelection = new ArrayList<SelectItem>();

		logSelection.add(new SelectItem("1", "Today"));
		logSelection.add(new SelectItem("7", "last 7 Days"));
		logSelection.add(new SelectItem("14", "last 14 Days"));
		logSelection.add(new SelectItem("30", "last 30 Days"));
		logSelection.add(new SelectItem("0", "Custom"));

		return logSelection;
	}

	public String getCurrentYear() {
		// compute default value
		if (currentYear == null || "".equals(currentYear)) {
			Calendar cal = Calendar.getInstance();

			currentYear = "" + cal.get(Calendar.YEAR);
		}
		return currentYear;
	}

	public void setCurrentYear(String currentYear) {
		this.currentYear = currentYear;
		trendByDay = null;
		trendByMonth = null;
	}

	public String getCurrentMonth() {
		// compute default value
		if (currentMonth == null || "".equals(currentMonth)) {
			Calendar cal = Calendar.getInstance();

			currentMonth = "" + cal.get(Calendar.MONTH);
		}
		return currentMonth;
	}

	public void setCurrentMonth(String currentMonth) {
		this.currentMonth = currentMonth;
		trendByDay = null;
		trendByMonth = null;
	}

	public String getCurrentMonthText() {
		getCurrentMonth();

		if ("0".equals(currentMonth))
			return "January";
		if ("1".equals(currentMonth))
			return "February";
		if ("2".equals(currentMonth))
			return "March";
		if ("3".equals(currentMonth))
			return "April";
		if ("4".equals(currentMonth))
			return "May";
		if ("5".equals(currentMonth))
			return "June";
		if ("6".equals(currentMonth))
			return "Jule";
		if ("7".equals(currentMonth))
			return "August";
		if ("8".equals(currentMonth))
			return "September";
		if ("9".equals(currentMonth))
			return "October";
		if ("10".equals(currentMonth))
			return "November";
		if ("11".equals(currentMonth))
			return "December";

		return "";
	}

	public String getTimeFrameText() {
		DateFormat timeFormat = new SimpleDateFormat("EEE dd.MMM.yyyy HH:mm:ss");
		String sText = "";
		sText += timeFormat.format(currentAnalyzer.getTimeFrom());
		sText += "   -   ";
		sText += timeFormat.format(currentAnalyzer.getTimeTo());

		return sText;

	}

	/**
	 * Computes the total hits grouped by request URI
	 * 
	 * @return
	 */
	public List<ArrayList<Object>> getTotalHitsRequestURI() {
		if (totalHitsRequestURI == null)
			totalHitsRequestURI = anaylzerService.computeTotalHitsRequestURI(
					currentAnalyzer, navRequest.startpos, navRequest.count);
		return totalHitsRequestURI;
	}

	public Navigator getNavRequest() {
		return navRequest;
	}

	/**
	 * Computes the total hits grouped by request URI
	 * 
	 * @return
	 */
	public List<ArrayList<Object>> getTotalHitsReferrer() {
		if (totalHitsReferrer == null)
			totalHitsReferrer = anaylzerService.computeTotalHitsReferrer(
					currentAnalyzer, navReferrer.startpos, navReferrer.count);
		return totalHitsReferrer;
	}

	public Navigator getNavReferrer() {
		return navReferrer;
	}

	/**
	 * Computes the total errors grouped by error
	 * 
	 * @return
	 */
	public List<ArrayList<Object>> getTotalErrors() {
		if (totalErrors == null)
			totalErrors = anaylzerService.computeTotalErrors(currentAnalyzer);

		return totalErrors;
	}

	/**
	 * Computes the total errors grouped by request uri for a specific error
	 * code
	 * 
	 * @return
	 */
	public List<ArrayList<Object>> getTotalErrorsByCode() {

		if (totalErrorsByCode == null)
			totalErrorsByCode = anaylzerService.computeTotalErrorsByCode(
					currentAnalyzer, errorCode);

		return totalErrorsByCode;
	}

	/**
	 * defines a dataRange key
	 * 
	 * <ul>
	 * <li>today (1)
	 * <li>last 7 days (2)
	 * <li>last 30 days (3)
	 * <li>this month (4)
	 * <li>this year (5)
	 * <li>custom (0)
	 * </ul>
	 * 
	 * @return
	 */
	public String getTimeRange() {
		return timeRange;
	}

	/**
	 * update the dateRange objects
	 * 
	 * @param adateRange
	 */
	public void setTimeRange(String adateRange) {
		this.timeRange = adateRange;
		// set date range objects
		currentAnalyzer.setTimeFrame(timeRange);

	}

	public String getErrorCode() {
		return errorCode;
	}

	public void setErrorCode(String errorCode) {
		this.errorCode = errorCode;
		totalErrorsByCode = null;
		
	}

	/**
	 * Computes the clients in %
	 * 
	 * @return
	 */
	public List<ArrayList<Object>> getBrowserType() {
		if (browserType == null) {
			browserType = anaylzerService.computeBrowserType(currentAnalyzer);
			// transform result in %
			long totalcount = 0;
			for (ArrayList<Object> row : browserType)
				totalcount += new Integer((row.get(1)).toString());

			for (ArrayList<Object> row : browserType) {
				float count = new Float((row.get(1)).toString());
				float p = count / totalcount * 100;
				row.set(1, Math.round(p));
			}
		}
		return browserType;
	}

	/**
	 * Computes the browser in %
	 * 
	 * @return
	 */
	public List<ArrayList<Object>> getBrowser() {
		List<ArrayList<Object>> result = anaylzerService
				.computeBrowser(currentAnalyzer);
		// transform result in %
		long totalcount = 0;
		for (ArrayList<Object> row : result)
			totalcount += new Integer((row.get(1)).toString());

		for (ArrayList<Object> row : result) {
			float count = new Float((row.get(1)).toString());
			float p = count / totalcount * 100;
			row.set(1, Math.round(p));
		}
		return result;
	}

	/**
	 * Computes the os in %
	 * 
	 * @return
	 */
	public List<ArrayList<Object>> getOperatingsystem() {
		if (operatingsystem == null) {
			operatingsystem = anaylzerService
					.computeOperatingsystem(currentAnalyzer);
			// transform result in %
			long totalcount = 0;
			for (ArrayList<Object> row : operatingsystem)
				totalcount += new Integer((row.get(1)).toString());

			for (ArrayList<Object> row : operatingsystem) {
				float count = new Float((row.get(1)).toString());
				float p = count / totalcount * 100;
				row.set(1, Math.round(p));
			}
		}
		return operatingsystem;
	}

	/**
	 * Computes the trend of total hits grouped by day for the current month
	 * 
	 * to each row the method adds a column with the computed % value of the
	 * total hits per day
	 * 
	 * Also the method fills up the ArrayList with a row for each day of month -
	 * also when computeTrendByDay() method did not returned a value for those
	 * days
	 * 
	 * @return
	 */
	public List<ArrayList<Object>> getTrendByDay() {

		if (trendByDay == null) {
			// get current month and current year
			int iMonth = 0;
			int iYear = 0;
			try {
				iMonth = Integer.parseInt(currentMonth);
				iYear = Integer.parseInt(currentYear);
			} catch (NumberFormatException en) {
				// error
				logger.warning("getTrendByDay can not format month / year");
			}

			List<ArrayList<Object>> result = anaylzerService.computeTrendByDay(
					currentAnalyzer, iMonth, iYear);

			// compute highest value
			long topcount = 0;
			for (ArrayList<Object> row : result) {
				long acount = new Long((row.get(1)).toString());

				if (acount > topcount)
					topcount = acount;
			}

			// add % column
			for (ArrayList<Object> row : result) {
				float count = new Float((row.get(1)).toString());
				// compute % to topcount
				float p = (count) / topcount * 100;
				// add new column
				row.add(Math.round(p));
			}

			// now create a array with all days of the current month

			trendByDay = new ArrayList<ArrayList<Object>>();
			Calendar cal = Calendar.getInstance();
			cal.set(Calendar.DAY_OF_MONTH, 1);
			cal.set(Calendar.MONTH, iMonth);
			cal.set(Calendar.YEAR, iYear);

			int lastDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);

			for (int i = 1; i <= lastDay; i++) {

				// test if we have a result for that day
				boolean dayFound = false;
				for (ArrayList<Object> row : result) {
					int currentday = new Integer(row.get(0).toString());
					if (currentday == i) {
						trendByDay.add(row);
						dayFound = true;
						break;
					}
				}

				if (!dayFound) {
					// add an empty day
					ArrayList<Object> emptyday = new ArrayList<Object>();
					emptyday.add(i);
					emptyday.add(0);
					emptyday.add(0);

					trendByDay.add(emptyday);
				}

			}
		}
		return trendByDay;
	}

	/**
	 * Computes the trend of total hits grouped month for the current year
	 * 
	 * to each row the method adds a column with the computed % value of the
	 * total hits per month
	 * 
	 * Also the method fills up the ArrayList with a row for each month of year
	 * - also when computeTrendByMonth() method did not returned a value for
	 * those month
	 * 
	 * @return
	 */
	public List<ArrayList<Object>> getTrendByMonth() {

		if (trendByMonth == null) {
			// get current month and current year
			int iYear = 0;
			try {
				iYear = Integer.parseInt(getCurrentYear());
			} catch (NumberFormatException en) {
				// error
				logger.warning("getTrendByDay can not format month / year");
			}

			List<ArrayList<Object>> result = anaylzerService
					.computeTrendByMonth(currentAnalyzer, iYear);

			// compute highest value
			long topcount = 0;
			for (ArrayList<Object> row : result) {
				long acount = new Long((row.get(1)).toString());

				if (acount > topcount)
					topcount = acount;
			}

			// add % column
			for (ArrayList<Object> row : result) {
				float count = new Float((row.get(1)).toString());
				// compute % to topcount
				float p = (count) / topcount * 100;
				// add new column
				row.add(Math.round(p));
			}

			// now create a array with all month of year

			trendByMonth = new ArrayList<ArrayList<Object>>();

			for (int i = 0; i < 12; i++) {

				// test if we have a result for that month
				boolean monthFound = false;
				for (ArrayList<Object> row : result) {
					int currentday = new Integer(row.get(0).toString());
					if (currentday == i) {
						trendByMonth.add(row);
						monthFound = true;
						break;
					}
				}

				if (!monthFound) {
					// add an empty day
					ArrayList<Object> emptymonth = new ArrayList<Object>();
					emptymonth.add(i);
					emptymonth.add(0);
					emptymonth.add(0);

					trendByMonth.add(emptymonth);
				}

			}

			// add the name of the mont in the 4 column
			trendByMonth.get(0).add("Jan.");
			trendByMonth.get(1).add("Feb.");
			trendByMonth.get(2).add("Mar.");
			trendByMonth.get(3).add("Apr.");
			trendByMonth.get(4).add("May.");
			trendByMonth.get(5).add("Jun.");
			trendByMonth.get(6).add("Jul.");
			trendByMonth.get(7).add("Aug.");
			trendByMonth.get(8).add("Sep.");
			trendByMonth.get(9).add("Oct.");
			trendByMonth.get(10).add("Nov.");
			trendByMonth.get(11).add("Dec.");
		}
		return trendByMonth;
	}

	/**
	 * this method is triggerd by the Year-Chart diagramm when a user clicks on
	 * a month. The mehtod expects the selected month in the param 'id'
	 * 
	 * @param event
	 */
	public void doSwitchMonth(ActionEvent event) {

		int month = -1;
		List children = event.getComponent().getChildren();
		for (int i = 0; i < children.size(); i++) {
			if (children.get(i) instanceof UIParameter) {
				UIParameter currentParam = (UIParameter) children.get(i);
				if (currentParam.getName().equals("id")
						&& currentParam.getValue() != null) {
					// Value can be provided as String or Integer Object
					month = Integer
							.parseInt(currentParam.getValue().toString());
					break;
				}
			}
		}

		if (month > -1)
			this.setCurrentMonth("" + month);

	}
}
